package com.ewing.core.jdbc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.ewing.core.jdbc.util.BeanListProcessHandler;
import com.ewing.core.jdbc.util.Count;
import com.ewing.core.jdbc.util.PageBean;
import com.ewing.utils.PageUtil;
import com.ewing.utils.SqlUtil;

@Repository("baseDao")
public class HibernateDaoImpl extends HibernateDaoSupport implements BaseDao {
  
  private static Logger logger = Logger.getLogger(HibernateDaoImpl.class);

  private void bulidEntityTime(Object entity, boolean isNew) {
    Object oldentity = null;
    try {
      Field id = entity.getClass().getDeclaredField("id");
      id.setAccessible(true);
      oldentity = findOne(Integer.valueOf(id.get(entity).toString()), entity.getClass());
    } catch (Exception e1) {
      logger.warn("error find id in " + entity.getClass().toString());
    }
    try {
      Field createtime = entity.getClass().getDeclaredField("createTime");
      createtime.setAccessible(true);

      if (isNew) {
        if (createtime.get(entity) == null)
          createtime.set(entity, new java.sql.Timestamp((new java.util.Date()).getTime()));
      } else {
        if (oldentity != null) {
          createtime = oldentity.getClass().getDeclaredField("createTime");
          createtime.setAccessible(true);
          createtime.set(entity, createtime.get(oldentity));
        }
      }
    } catch (Exception e) {
      logger.warn("error in bulid  createtime in " + entity.getClass().toString());
    }
    try {

      Field last_update = entity.getClass().getDeclaredField("lastUpdate");
      if (last_update != null) {
        last_update.setAccessible(true);
        last_update.set(entity, new java.sql.Timestamp((new java.util.Date()).getTime()));
      }
    } catch (Exception e) {
      logger.warn("error bulid  last_update in " + entity.getClass().toString());
    }
  }

  @Override
  public void delete(Object entity) {
    try {
      Method method = entity.getClass().getMethod("getId");
      Object primaryId = method.invoke(entity);
      if (primaryId == null)
        throw new DaoException("the primary key should not be null");
      Object object = findOne((Integer) primaryId, entity.getClass());
      getHibernateTemplate().delete(object);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

  }

  @SuppressWarnings("deprecation")
  @Override
  public void executeSql(String sql) {
    Session session = null;
    Connection conn = null;
    Statement stmt = null;
    try {
      session = this.getSession();
      conn = session.connection();
      stmt = conn.createStatement();
      boolean ret = stmt.execute(sql);
      logger.info("executeSql sql:" + sql + "");
      logger.info("executeSql result:" + ret + "");
    } catch (Exception e) {
      logger.error("fail to execute sql:" + sql, e);
    } finally {
      this.releaseSession(session);
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> List<T> find(String condition, Class<T> entityClass) {
    try {
      getHibernateTemplate().setCacheQueries(true);
      List<T> list = getHibernateTemplate().find(generateQuerySql(condition, entityClass));
      return list;
    } catch (DataAccessException e) {
      logger.error(e, e);
    }
    return null;
  }

  /**
   * @author Joeson
   */
  public <T> List<T> findMuti(List<Integer> idList, Class<T> entityClass) {
    if (CollectionUtils.isEmpty(idList) || null == entityClass) {
      return Collections.emptyList();
    }

    List<T> list = find("id in (" + StringUtils.join(idList.toArray(), ",") + ")", entityClass);
    if (CollectionUtils.isNotEmpty(list))
      return list;
    return Collections.emptyList();
  }

  @Override
  public <T> T findOne(Integer id, Class<T> entityClass) {
    if (null == id || null == entityClass) {
      return null;
    }

    List<T> list = find("id=" + id, entityClass);
    if (!list.isEmpty())
      return (T) list.get(0);
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T findOne(String condition, Class<T> entityClass) {
    try {
      getHibernateTemplate().setCacheQueries(true);
      List<T> list = getHibernateTemplate().find(generateQuerySql(condition, entityClass));
      if (!list.isEmpty())
        return list.get(0);
    } catch (DataAccessException e) {
      logger.error(e, e);
    }
    return null;
  }

  /**
   * 根本查询条件创建SQL
   * 
   * @param condition
   * @param entityClass
   * @return
   */
  @SuppressWarnings("unused")
  private <T> String generateQueryCountSql(String condition, Class<T> entityClass) {
    StringBuffer sql = new StringBuffer();
    sql.append("select * from ").append(entityClass.getName());
    if (!StringUtils.isEmpty(condition))
      sql.append(" where ").append(condition);
    return "select count(*) from ( " + sql.toString() + " )";
  }

  /**
   * 根本查询条件创建SQL
   * 
   * @param condition
   * @param entityClass
   * @return
   */
  private <T> String generateQuerySql(String condition, Class<T> entityClass) {
    return generateQuerySql(condition, "", entityClass);
  }

  /**
   * 根本查询条件创建SQL
   * 
   * @param condition
   * @param entityClass
   * @return
   */
  private <T> String generateQuerySql(String condition, String orderBy, Class<T> entityClass) {
    StringBuffer sql = new StringBuffer();
    sql.append(" from ").append(entityClass.getName());
    orderBy = orderBy != null ? " " + orderBy : "";
    if (!StringUtils.isEmpty(condition)) {
      if (condition.trim().startsWith("select"))
        return condition + orderBy;
      if (condition.trim().startsWith("from"))
        return condition + orderBy;

      sql.append(" where 1=1 ");
      if (!(condition.trim().startsWith("and") || condition.trim().startsWith("AND"))) {
        sql.append(" and ");
      }
      sql.append(condition);
    }
    sql.append(orderBy);
    return sql.toString();
  }

  @Override
  public Session getConnectionSession() {
    return this.getSession();
  }

  @SuppressWarnings("deprecation")
  @Override
  public <T> List<T> noMappedObjectQuery(String sql, Class<T> beanClass) {
    Connection connection = null;
    Statement stmt = null;
    ResultSet rs = null;
    Session session = null;
    try {
      session = this.getSession();
      connection = session.connection();
      stmt = connection.createStatement();
      rs = stmt.executeQuery(sql);
      BeanListProcessHandler beanListProcessHandler = new BeanListProcessHandler();
      return beanListProcessHandler.toBeanList(rs, beanClass);
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      this.releaseSession(session);
    }

  }

  @SuppressWarnings("deprecation")
  @Override
  public <T> PageBean<T> noMappedObjectPageQuery(String sql, Class<T> beanClass, Integer page,
      Integer pageSize) {
    Connection connection = null;
    Statement stmt = null;
    ResultSet rs = null;
    Session session = null;
    try {
      final Integer limit = PageUtil.getLimit(page, pageSize);
      final Integer start = PageUtil.getOffset(page, pageSize);
      String pageSql = sql + " limit " + start + "," + limit;
      logger.info("sql : " + pageSql);
      
      session = this.getSession();
      connection = session.connection();
      stmt = connection.createStatement();
      rs = stmt.executeQuery(pageSql);
      BeanListProcessHandler beanListProcessHandler = new BeanListProcessHandler();
      List<T> list = beanListProcessHandler.toBeanList(rs, beanClass);
      Integer count = noMappedObjectCountQuery(sql);
      return new PageBean<T>(page, count, pageSize, list);
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      this.releaseSession(session);
    }

  }

  @SuppressWarnings("deprecation")
  public Integer noMappedObjectCountQuery(String sql) {
    Connection connection = null;
    Statement stmt = null;
    ResultSet rs = null;
    Session session = null;
    try {
      String coutSql = SqlUtil.generateCountSql(sql);
      session = this.getSession();
      connection = session.connection();
      stmt = connection.createStatement();
      rs = stmt.executeQuery(coutSql);
      BeanListProcessHandler beanListProcessHandler = new BeanListProcessHandler();
      List<Count> count = beanListProcessHandler.toBeanList(rs, Count.class);
      if (count == null || count.get(0) == null || count.get(0).getCount() == null)
        return 0;
      return count.get(0).getCount();
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      this.releaseSession(session);
    }

  }

  @Override
  public <T> PageBean<T> pageQuery(final String condition, Integer pageSize, Integer page,
      Class<T> entityClass) {
    return pageQuery(condition, "", pageSize, page, entityClass);
  }

  @SuppressWarnings({"unchecked"})
  @Override
  public <T> PageBean<T> pageQuery(final String condition, String orderBy, Integer pageSize,
      Integer page, Class<T> entityClass) {

    final Integer limit = PageUtil.getLimit(page, pageSize);
    final Integer start = PageUtil.getOffset(page, pageSize);

    final String _orderBy = orderBy;
    final Class<T> entity = entityClass;
    
    PageBean<T> ps = super.getHibernateTemplate().execute(new HibernateCallback<PageBean<T>>() {
      @Override
      public PageBean<T> doInHibernate(Session s) throws HibernateException, SQLException {
        Query rowCountQuery = s.createQuery(generateQuerySql(condition, _orderBy, entity));
        int totalCount = rowCountQuery.list().size();
        Query pageQuery =
            s.createQuery(generateQuerySql(condition, _orderBy, entity)).setMaxResults(limit)
                .setFirstResult(start);
        PageBean<T> ps = new PageBean<T>(start, totalCount, limit, pageQuery.list());
        return ps;
      }
    });
    return  ps;
  }

  @Override
  public <T> PageBean<T> pageQuery(String hql, String condition, String orderBy, Integer pageSize,
      Integer page, Class<T> entityClass) {
    if (!StringUtils.isEmpty(condition))
      hql += condition;
    if (!StringUtils.isEmpty(orderBy))
      hql += orderBy;
    return pageQuery(hql, pageSize, page, entityClass);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T queryObject(String sql, Class<T> queryClass) {
    List<T> list = getHibernateTemplate().find(sql);
    if (!list.isEmpty()) {
      if (list.size() > 1)
        throw new RuntimeException("result more than one , is not legal.");
      return (T) list.get(0);
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> List<T> queryBySql(final String sql, Class<T> queryClass) {
    return getHibernateTemplate().find(sql);
//    List<T> list = getHibernateTemplate().executeFind(new HibernateCallback<List<T>>() {
//      public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
//        Query query = session.createSQLQuery(sql);
//        List<T> list = query.list();
//        return list;
//      }
//    });
//    return list;
  }

  @Override
  public void releaseConnectionSession(Session session) {
    this.releaseSession(session);
  }

  @Override
  public void save(Object entity) {
    bulidEntityTime(entity, true);
    getHibernateTemplate().persist(entity);
  }
  
  @Override
  public void saveOrUpdate(Object entity){
    bulidEntityTime(entity, true);
    getHibernateTemplate().saveOrUpdate(entity);
  }

  @Override
  public <T> void saveMuti(List<T> entityList) {
    if (CollectionUtils.isEmpty(entityList)) {
      return;
    }
    for (Object entity : entityList) {
      bulidEntityTime(entity, true);
    }

    getHibernateTemplate().saveOrUpdateAll(entityList);
  }

  @Override
  public void update(Object entity) {
    bulidEntityTime(entity, false);
    getHibernateTemplate().update(getHibernateTemplate().merge(entity));
  }

  @Override
  public <T> void updateMuti(List<T> entityList) {
    if (CollectionUtils.isEmpty(entityList)) {
      return;
    }
    for (Object entity : entityList) {
      bulidEntityTime(entity, true);
    }

    getHibernateTemplate().saveOrUpdateAll(entityList);
  }

  @Override
  public <T> List<T> findAll(Class<T> entityClass) {
    if (null == entityClass) {
      return Collections.emptyList();
    }

    return find(StringUtils.EMPTY, entityClass);
  }

}
